//----------------------------------------------------------------------------
// user_logic.vhd - module
//----------------------------------------------------------------------------
//
// ***************************************************************************
// ** Copyright (c) 1995-2010 Xilinx, Inc.  All rights reserved.            **
// **                                                                       **
// ** Xilinx, Inc.                                                          **
// ** XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"         **
// ** AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND       **
// ** SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,        **
// ** OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,        **
// ** APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION           **
// ** THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,     **
// ** AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE      **
// ** FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY              **
// ** WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE               **
// ** IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR        **
// ** REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF       **
// ** INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS       **
// ** FOR A PARTICULAR PURPOSE.                                             **
// **                                                                       **
// ***************************************************************************
//
//----------------------------------------------------------------------------
// Filename:          user_logic.vhd
// Version:           1.00.a
// Description:       User logic module.
// Date:              Thu Feb 13 22:13:12 2014 (by Create and Import Peripheral Wizard)
// Verilog Standard:  Verilog-2001
//----------------------------------------------------------------------------
// Naming Conventions:
//   active low signals:                    "*_n"
//   clock signals:                         "clk", "clk_div#", "clk_#x"
//   reset signals:                         "rst", "rst_n"
//   generics:                              "C_*"
//   user defined types:                    "*_TYPE"
//   state machine next state:              "*_ns"
//   state machine current state:           "*_cs"
//   combinatorial signals:                 "*_com"
//   pipelined or register delay signals:   "*_d#"
//   counter signals:                       "*cnt*"
//   clock enable signals:                  "*_ce"
//   internal version of output port:       "*_i"
//   device pins:                           "*_pin"
//   ports:                                 "- Names begin with Uppercase"
//   processes:                             "*_PROCESS"
//   component instantiations:              "<ENTITY_>I_<#|FUNC>"
//----------------------------------------------------------------------------



module user_logic
(
  // -- ADD USER PORTS BELOW THIS LINE ---------------
  SCL,
  SDA,
  // -- ADD USER PORTS ABOVE THIS LINE ---------------

  // -- DO NOT EDIT BELOW THIS LINE ------------------
  // -- Bus protocol ports, do not add to or delete 
  Bus2IP_Clk,                     // Bus to IP clock
  Bus2IP_Reset,                   // Bus to IP reset
  Bus2IP_Data,                    // Bus to IP data bus
  Bus2IP_BE,                      // Bus to IP byte enables
  Bus2IP_RdCE,                    // Bus to IP read chip enable
  Bus2IP_WrCE,                    // Bus to IP write chip enable
  IP2Bus_Data,                    // IP to Bus data bus
  IP2Bus_RdAck,                   // IP to Bus read transfer acknowledgement
  IP2Bus_WrAck,                   // IP to Bus write transfer acknowledgement
  IP2Bus_Error                    // IP to Bus error response
  // -- DO NOT EDIT ABOVE THIS LINE ------------------
); // user_logic

// -- ADD USER PARAMETERS BELOW THIS LINE ------------
// --USER parameters added here 
// -- ADD USER PARAMETERS ABOVE THIS LINE ------------

// -- DO NOT EDIT BELOW THIS LINE --------------------
// -- Bus protocol parameters, do not add to or delete
parameter C_SLV_DWIDTH                   = 32;
parameter C_NUM_REG                      = 4;
// -- DO NOT EDIT ABOVE THIS LINE --------------------

// -- ADD USER PORTS BELOW THIS LINE -----------------
inout                                    SDA; 
inout                                    SCL;
// -- ADD USER PORTS ABOVE THIS LINE -----------------

// -- DO NOT EDIT BELOW THIS LINE --------------------
// -- Bus protocol ports, do not add to or delete
input                                     Bus2IP_Clk;
input                                     Bus2IP_Reset;
input      [0 : C_SLV_DWIDTH-1]           Bus2IP_Data;
input      [0 : C_SLV_DWIDTH/8-1]         Bus2IP_BE;
input      [0 : C_NUM_REG-1]              Bus2IP_RdCE;
input      [0 : C_NUM_REG-1]              Bus2IP_WrCE;
output     [0 : C_SLV_DWIDTH-1]           IP2Bus_Data;
output                                    IP2Bus_RdAck;
output                                    IP2Bus_WrAck;
output                                    IP2Bus_Error;
// -- DO NOT EDIT ABOVE THIS LINE --------------------
localparam LS_IDLE = 1;
localparam LS_WR_CA = 2;   //write first bye of adress
localparam LS_WR_A1 = 3;   //write second byte of address 
localparam LS_WR_A2 = 4;   //write second byte of address 
localparam LS_WR_D = 5;    //write data byte
localparam LS_RD_D = 6;    //read  data byte
localparam LS_WAIT_STOP_R = 7;  //wait for stop after reading
localparam LS_WAIT_STOP_W = 8;  //wait for stop after writing
localparam LS_RD_D_1 = 9;       //step 1 for read
localparam LS_RD_D_2 = 10;      //step 2 for read
localparam LS_SKIP_READY = 11;  

// -- command register definitions reg[2]--------------------
localparam L_START_BIT = 30;   // 'start' bit of command register
localparam L_READ_WRITE_BIT = 31; // 'read/write' bit of command register

// -- statuc register definitions reg[3] ---------------------
localparam L_I2C_READY_BIT = 31; // 'read/write' bit of command register
localparam L_READ_ACK_BIT = 30;  // the ack of 'read'

//--- status bits definitions ------------------------
localparam L_START_FLAG = 1;    // 'start' bit is 1 when i2c starts to transfer bytes.
localparam L_WRITE_ENABLED = 0; // if write is enabled, set it to '0'
localparam L_SEND_ACK_ENABLED = 1; // sending ack is enabled.
//----------------------------------------------------------------------------
// Implementation
//----------------------------------------------------------------------------

  // --USER nets declarations added here, as needed for user logic
  reg                                      i2c_start;
  reg                                      i2c_stop;
  reg                                      i2c_read;
  reg                                      i2c_write;
  reg                                      i2c_send_ack;
  reg        [7 : 0]                        i2c_data_in;
  
  reg        [3 : 0]                        i2c_master_state;
  
  wire                                      i2c_free;
  wire                                      i2c_ready;
  wire       [5 : 0]                        i2c_core_state;
  wire                                      i2c_rec_ack;
  wire       [7 : 0]                        i2c_data_out;
  
  // Nets for user logic slave model s/w accessible register example
  reg        [C_SLV_DWIDTH-1 : 0]           slv_reg_addr;
  reg        [C_SLV_DWIDTH-1 : 0]           slv_reg_wr;
  reg        [C_SLV_DWIDTH-1 : 0]           slv_reg_cmd;
  reg                                       i2c_busy;
  reg                                       reg_working;
  reg        [C_SLV_DWIDTH-1 : 0]           slv_reg_rd;
  wire       [0 : 3]                        slv_reg_write_sel;
  wire       [0 : 3]                        slv_reg_read_sel;
  reg        [C_SLV_DWIDTH-1 : 0]           slv_ip2bus_data;
  wire                                      slv_read_ack;
  wire                                      slv_write_ack;
  integer                                   byte_index, bit_index;
 

  // --USER logic implementation added here
  i2c_master_v01 #(25000000, 100000) I2C_MASTER_V01(
      .sys_clk(Bus2IP_Clk),
      .sys_rst(Bus2IP_Reset),
      .start(i2c_start),
      .stop(i2c_stop),
      .read(i2c_read),
      .write(i2c_write),
      .send_ack(i2c_send_ack),
      .mstr_din(i2c_data_in),
      .sda(SDA),
      .scl(SCL),
      
      .free(i2c_free),
      .rec_ack(i2c_rec_ack),
      .ready(i2c_ready),
      .core_state(i2c_core_state),
      .mstr_dout(i2c_data_out)
  );
  
  // ------------------------------------------------------
  // Example code to read/write user logic slave model s/w accessible registers
  // 
  // Note:
  // The example code presented here is to show you one way of reading/writing
  // software accessible registers implemented in the user logic slave model.
  // Each bit of the Bus2IP_WrCE/Bus2IP_RdCE signals is configured to correspond
  // to one software accessible register by the top level template. For example,
  // if you have four 32 bit software accessible registers in the user logic,
  // you are basically operating on the following memory mapped registers:
  // 
  //    Bus2IP_WrCE/Bus2IP_RdCE   Memory Mapped Register
  //                     "1000"   C_BASEADDR + 0x0    // chip address + register address
  //                     "0100"   C_BASEADDR + 0x4    // data register
  //                     "0010"   C_BASEADDR + 0x8    // command register
  //                     "0001"   C_BASEADDR + 0xC    // status register
  // 
  // ------------------------------------------------------

  assign
    slv_reg_write_sel = Bus2IP_WrCE[0:3],
    slv_reg_read_sel  = Bus2IP_RdCE[0:3],
    slv_write_ack     = Bus2IP_WrCE[0] || Bus2IP_WrCE[1] || Bus2IP_WrCE[2] || Bus2IP_WrCE[3],
    slv_read_ack      = Bus2IP_RdCE[0] || Bus2IP_RdCE[1] || Bus2IP_RdCE[2] || Bus2IP_RdCE[3];
    
    always @(i2c_ready)
        reg_working <= i2c_ready;
	 
  always @( posedge Bus2IP_Clk )
    begin: SLAVE_REG_WRITE_PROC
      if ( Bus2IP_Reset == 1 )
        begin
          slv_reg_addr <= 0;
          slv_reg_wr <= 0;
        end
      else
        case ( slv_reg_write_sel )
          4'b1000 :
            for ( byte_index = (C_SLV_DWIDTH/8)- 1; byte_index >= 0; byte_index = byte_index-1 )
              if ( Bus2IP_BE[ byte_index ] == 1 )
                for ( bit_index = byte_index*8+7; bit_index >= byte_index*8; bit_index = bit_index-1 )
                  slv_reg_addr[bit_index] <= Bus2IP_Data[bit_index];
		
          4'b0100 :
            for ( byte_index = (C_SLV_DWIDTH/8)- 1; byte_index >= 0; byte_index = byte_index-1 )
              if ( Bus2IP_BE[ byte_index ] == 1 )
                for ( bit_index = byte_index*8+7; bit_index >= byte_index*8; bit_index = bit_index-1 )
                  slv_reg_wr[bit_index] <= Bus2IP_Data[bit_index];
          4'b0010 :
            for ( byte_index = (C_SLV_DWIDTH/8)- 1; byte_index >= 0; byte_index = byte_index-1 )
              if ( Bus2IP_BE[ byte_index ] == 1 )
                for ( bit_index = byte_index*8+7; bit_index >= byte_index*8; bit_index = bit_index-1 )
                  slv_reg_cmd[bit_index] <= Bus2IP_Data[bit_index];
          4'b0001 : ;
        endcase
    end // SLAVE_REG_WRITE_PROC
	 
	task send_byte;
       input [6:0] data;
       input  read_write_bit;
       begin
          i2c_data_in <= {data, read_write_bit};  //write chip address
          i2c_read <= 0;
          i2c_write <= 1;
          i2c_start <= 1;
          i2c_stop <= 0;
       end
	 endtask
     
     task clear_flags;
     begin
         i2c_write <= 0;
         i2c_read <= 0;
         i2c_start <= 0;
         i2c_stop <= 0;
     end
     endtask;

	 task send_byte_with_check;
       input [7:0] data;
	   input [7:0] target_state;
       begin
         if (reg_working == 1)
		 begin
           i2c_data_in <= data;  //write chip address
           i2c_read <= 0;
           i2c_write <= 1;
           i2c_start <= 0;
           i2c_stop <= 0;
           reg_working <= 0;
           i2c_master_state <= target_state;
         end
       end  
	 endtask
     
     always @( posedge Bus2IP_Clk )
        if ( Bus2IP_Reset == 1 )
        begin
            i2c_master_state <= LS_IDLE;
            i2c_send_ack <= L_SEND_ACK_ENABLED;
            i2c_busy <= 0;
            slv_reg_rd <= 0;
            reg_working <= 0;
        end
        else
	    begin
          case (i2c_master_state)
			 LS_IDLE: begin
    		    i2c_master_state <= LS_IDLE;
                i2c_busy <= 0;
                
			    if (slv_reg_cmd[L_START_BIT] == L_START_FLAG) //'start' is detected
				 begin
				     i2c_master_state <= LS_WR_CA;
                     //slv_reg_cmd[L_START_BIT] <= 0;  //clear bit of 'Start'
                     reg_working <= 1;
				 end
             end
          
			 //write first byte of address
			 LS_WR_CA: begin
			    if (reg_working == 1)
                begin
				   send_byte(slv_reg_addr[22 : 16], slv_reg_cmd[L_READ_WRITE_BIT]);
                   reg_working <= 0;
   		           i2c_master_state <= LS_SKIP_READY;
                end   
             end
             
             LS_SKIP_READY: begin
                if (reg_working == 1)
                begin
                   reg_working <= 0;
                   i2c_master_state <= LS_WR_A1;
                end
             end   
 
			 //write second byte of address
		      LS_WR_A1: begin
                clear_flags();
			    send_byte_with_check(slv_reg_addr[15 : 8], LS_WR_A2);
              end  
			 
			 LS_WR_A2: begin
                clear_flags();
			    if (slv_reg_cmd[L_READ_WRITE_BIT] == L_WRITE_ENABLED) //'read/write' == write
			        send_byte_with_check(slv_reg_addr[7 : 0], LS_WR_D);
                else
                    send_byte_with_check(slv_reg_addr[7 : 0], LS_RD_D_1);                
             end       
				 
             LS_WR_D: begin
                clear_flags();
			    send_byte_with_check(slv_reg_wr[7 : 0], LS_WAIT_STOP_W);
             end   
             
             LS_RD_D_1: begin
                clear_flags();
                if (reg_working == 1)
                begin
                   send_byte(slv_reg_addr[22:16], 1); //read data.
                   reg_working <= 0;
                   i2c_master_state <= LS_RD_D_2;
                end 
             end

             LS_RD_D_2: begin
                clear_flags();
                if (reg_working == 1)
                begin
                   slv_reg_rd[7:0] <= i2c_data_in;
                   i2c_master_state <= LS_WAIT_STOP_R;
                   reg_working <= 0;
                end
             end
             
             LS_WAIT_STOP_R: begin
                 clear_flags();
                 if (reg_working == 1)
                 begin
                     i2c_start <= 0;
                     i2c_stop <= 1;
                     i2c_busy <= 1;
                 end
             end    

             LS_WAIT_STOP_W: begin
                 clear_flags();
                 if (reg_working == 1)
                 begin
                     i2c_start <= 0;
                     i2c_stop <= 1;
                     i2c_busy <= 1;
                 end
		     end
	         default: ;
           endcase           
         end    

    
  always @( slv_reg_read_sel or i2c_busy or i2c_rec_ack or slv_reg_rd)
    begin: SLAVE_REG_READ_PROC
      case ( slv_reg_read_sel )
        4'b0001 : begin
           slv_ip2bus_data[L_I2C_READY_BIT] <= i2c_busy;
           slv_ip2bus_data[L_READ_ACK_BIT] <= i2c_rec_ack;
           slv_ip2bus_data[29 : 0] <= 0;
        end
        
        4'b0100 : slv_ip2bus_data <= slv_reg_rd;
        default : slv_ip2bus_data <= 0;
      endcase
	end 


  // ------------------------------------------------------------
  // Example code to drive IP to Bus signals
  // ------------------------------------------------------------

  assign IP2Bus_Data    = slv_ip2bus_data;
  assign IP2Bus_WrAck   = slv_write_ack;
  assign IP2Bus_RdAck   = slv_read_ack;
  assign IP2Bus_Error   = 0;

endmodule
